package com.hx.eaz.domains

import com.hx.eaz.models.AnalyzeQuery
import com.hx.eaz.models.CompanyInfo
import com.hx.eaz.models.MatchType
import java.lang.IllegalArgumentException
import java.math.BigDecimal


/**
 * 分析分类
 *
 */
enum class AnalyzeType(val tag: String) {
    COMPANY_COUNT("企业合计"),
    COMPANY_CHANGE("排名变化"),
    COMPANY_IN_COME("企业收入"),
    COMPANY_PROFIT("企业利润")
}

/**
 * 分析器
 */
class Analyzer(private val analyzeQuery: AnalyzeQuery,
               /**
                * 数据缓存
                *
                * String 为数据集合名称
                * List<CompanyInfo>为被缓存的企业信息
                *
                * 表示一个数据集中的企业信息
                */
               private val cache: Map<String, List<CompanyInfo>>) {

    companion object {

        fun createAnalyzeResults(title: String, groupedInfoList: Map<String, List<CompanyInfo>>): SumAnalyzeSet {
            val list = AnalyzeType.values().map { createAnalyzeResultByAnalyzeType(it, groupedInfoList) }
            return SumAnalyzeSet(title, list)
        }

        private fun createAnalyzeResultByAnalyzeType(type: AnalyzeType, groupedInfoList: Map<String, List<CompanyInfo>> ): Pair<String, List<SumAnalyze>> {
            return when(type) {
                AnalyzeType.COMPANY_COUNT -> type.tag to groupedInfoList.map { SumAnalyze(it.key, it.value.size.toBigDecimal()) }.sortedBy { -it.sum }
                AnalyzeType.COMPANY_CHANGE -> type.tag to groupedInfoList.map { SumAnalyze(it.key, it.value.map { c -> c.sortChange() }.sum().toBigDecimal()) }.sortedBy { -it.sum }
                AnalyzeType.COMPANY_IN_COME -> type.tag to groupedInfoList.map { SumAnalyze(it.key, decimalReduce(it.value.map { c -> c.inCome })) }.sortedBy { -it.sum }
                AnalyzeType.COMPANY_PROFIT -> type.tag to groupedInfoList.map { SumAnalyze(it.key, decimalReduce(it.value.map { c -> c.profit })) }.sortedBy { -it.sum }
            }
        }

        private fun decimalReduce(vals: List<BigDecimal>): BigDecimal {
            return if (vals.isEmpty()) {
                BigDecimal.ZERO
            }else{
                vals.reduce(BigDecimal::add)
            }
        }

        fun reduce(setList: List<SumAnalyzeSet>, analyzeQuery: AnalyzeQuery): YearlyCountrySumAnalyzeSet {
            val title = setList.joinToString(",") { it.title }
            val years = setList.map { it.title }.sortedBy { it.replace(Regex("\\D"), "").toInt() }
            val comCount = fillCategoryByItems(AnalyzeType.COMPANY_COUNT.tag, setList)
            val names = comCount.second.last().sums.sortedBy { -it.sum }.take(5).map { it.name }
            val data = listOf(
                    comCount,
                    fillCategoryByItems(AnalyzeType.COMPANY_CHANGE.tag, setList),
                    fillCategoryByItems(AnalyzeType.COMPANY_IN_COME.tag, setList),
                    fillCategoryByItems(AnalyzeType.COMPANY_PROFIT.tag, setList)
            )
            val countries = if (analyzeQuery.matchType == MatchType.COUNTRY) names else analyzeQuery.countrySet.toList()
            val cateList = if (analyzeQuery.matchType == MatchType.CATEGORY) names else analyzeQuery.cateSet.toList()
            return YearlyCountrySumAnalyzeSet(title, years, countries, cateList, data)
        }

        private fun fillCategoryByItems(cate: String, setList: List<SumAnalyzeSet>): Pair<String, List<YearlyCountrySumAnalyze>> {
            return cate to setList.flatMap { set ->
                set.data.filter { pr -> pr.first == cate }
                        .map { YearlyCountrySumAnalyze(set.title, it.second) }
            }.sortedBy { it.year.replace(Regex("\\D"), "").toInt() }
        }
    }

    fun analyze(): AnalyzeResult{
        return when(analyzeQuery.matchType) {
            MatchType.COUNTRY -> analyzeByCountry()
            MatchType.CATEGORY -> analyzeByCategory()
        }
    }

    private fun analyzeByCategory(): AnalyzeResult {
        val dataSet = analyzeQuery.dataSet.map { it to cacheList(it) }
        if (dataSet.isNotEmpty()) {
            val ret = dataSet.map {
                val comList = filterByCountryAndCategory(it.second)
                val cates = findQueryCategorySet()
                val groupedMap = cates.map { ct ->
                    ct to comList.filter { c ->
                        c.cateList.contains(ct)
                    }
                }.toMap()
                createAnalyzeResults(it.first, groupedMap)
            }
            if (ret.size == 1) {
                return AnalyzeResult(ret[0], AnalyzeResType.SINGLE)
            }
            return AnalyzeResult( reduce(ret, analyzeQuery) , AnalyzeResType.MULTI)
        }
        throw IllegalArgumentException("non data set be filtered.")
    }

    private fun findQueryCategorySet(): Set<String> {
        var cateSet = analyzeQuery.cateSet.toSet()
        if (cateSet.isEmpty()) {
            cateSet = cache.flatMap { e -> e.value }.flatMap { c -> c.cateList }.toSet()
        }
        return cateSet
    }

    private fun analyzeByCountry(): AnalyzeResult {
        val datas = analyzeQuery.dataSet.map { it to cacheList(it) }
        if (datas.isNotEmpty()) {
            val ret = datas.map {
                createAnalyzeResults(it.first, filterByCountryAndCategory(it.second).groupBy { com -> com.country })
            }
            if (ret.size == 1) {
                return AnalyzeResult(ret[0], AnalyzeResType.SINGLE)
            }
            return AnalyzeResult( reduce(ret, analyzeQuery) , AnalyzeResType.MULTI)
        }
        throw IllegalArgumentException("non data set be filtered.")
    }

    private fun cacheList(key: String): List<CompanyInfo> {
        return cache[key] ?: listOf()
    }

    private fun filterByCountryAndCategory(companyList: List<CompanyInfo>): List<CompanyInfo> {
        return filterByCountrySet(filterByCateSet(companyList))
    }

    private fun filterByCountrySet(comInfos: List<CompanyInfo>): List<CompanyInfo> {
        return if (analyzeQuery.countrySet.isEmpty()) {
            comInfos
        } else {
            comInfos.filter { c -> analyzeQuery.countrySet.contains(c.country) }
        }
    }

    private fun filterByCateSet(comInfos: List<CompanyInfo>): List<CompanyInfo> {
        return if (analyzeQuery.cateSet.isEmpty()) {
            comInfos
        } else {
            comInfos.filter { c ->
                c.cateList.any { ct ->
                    analyzeQuery.cateSet.contains(ct)
                }
            }
        }
    }
}

data class YearlyCountrySumAnalyze(val year: String, val sums: List<SumAnalyze>)

data class YearlyCountrySumAnalyzeSet(val title: String,
                                      val years: List<String>,
                                      val countries: List<String>,
                                      val cateList: List<String>,
                                      val data: List<Pair<String, List<YearlyCountrySumAnalyze>>>
)

data class SumAnalyze(val name: String, val sum: BigDecimal)

data class SumAnalyzeSet(val title: String, val data: List<Pair<String, List<SumAnalyze>>>)

data class AnalyzeResult(val data: Any, val resType: AnalyzeResType)

enum class AnalyzeResType {
    SINGLE,
    MULTI
}